﻿using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using Microsoft.Research.DynamicDataDisplay.DataSources;
using Microsoft.Research.DynamicDataDisplay.PointMarkers;
using System;
using System.Linq;
using System.Windows.Threading;
using System.Collections.ObjectModel;
using Microsoft.Research.DynamicDataDisplay;

namespace CustomControls.Controls
{
    /// <summary>
    /// LineChart.xaml 的交互逻辑
    /// </summary>
    public partial class LineChart : ContentControl
    {
        /// <summary>
        /// line数据源
        /// </summary>
        public Dictionary<string, NormalLineInfo> LineDictionary = new Dictionary<string, NormalLineInfo>();
        /// <summary>
        /// 异步更新line数据源
        /// </summary>
        public Dictionary<string, AynscLineInfo> AynscLineLineDictionary = new Dictionary<string, AynscLineInfo>();
        /// <summary>
        /// Markline数据源
        /// </summary>
        public Dictionary<string, MarkLineInfo> MarkLineDictionary = new Dictionary<string, MarkLineInfo>();

        #region DependencyProperty
        public static readonly DependencyProperty AccentProperty = DependencyProperty.Register("Accent", typeof(string), typeof(LineChart), new FrameworkPropertyMetadata(string.Empty, AccentChanged));

        public string Accent
        {
            get { return this.GetValue(AccentProperty).ToString(); }
            set { this.SetValue(AccentProperty, value); }
        }

        private static void AccentChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
        {
            var lineChart = (LineChart)dependencyObject;
            lineChart.SetAccent((string)e.NewValue);
        }
        #endregion

        private bool HeaderFlag, XTitleFlag, YTitleFlag;
        private ChartPlotter lineChartPlotter;
        private DataTransFormat _dataTrans;
        /// <summary>
        /// 默认构造函数
        /// </summary>
        public LineChart(DataTransFormat dataTrans)
        {
            _dataTrans = dataTrans;
            switch (dataTrans)
            {
                case DataTransFormat.Log10XTransform:
                    DataTransforms.SetDefaultDataTransform(DataTransFormat.Log10XTransform);
                    lineChartPlotter = new LogXChartPlotter();
                    break;
                case DataTransFormat.DelegateDataTransform:
                    DataTransforms.SetDefaultDataTransform(DataTransFormat.IdentityTransform);
                    lineChartPlotter = new TimeChartPlotter();
                    break;
                case DataTransFormat.PolarToRectTransform:
                    DataTransforms.SetDefaultDataTransform(DataTransFormat.PolarToRectTransform);
                    lineChartPlotter = new ChartPlotter();
                    break;
                default:
                    DataTransforms.SetDefaultDataTransform(DataTransFormat.IdentityTransform);
                    lineChartPlotter = new ChartPlotter();
                    break;
            }
            SetAccent("Dark");
        }
        /// <summary>
        /// 是否显示浮动框
        /// </summary>
        /// <param name="flag"></param>
        public void SetLegendVisible(bool flag)
        {
            lineChartPlotter.Legend.AutoShowAndHide = false;
        }
        /// <summary>
        /// 默认构造函数
        /// </summary>
        public LineChart()
        {
            lineChartPlotter = new ChartPlotter();
            SetAccent("Dark");
        }
        /// <summary>
        /// 修改背景
        /// </summary>
        /// <param name="accent"></param>
        public void SetDefaultAccent()
        {
            lineChartPlotter.horizontalAxis.LineAndLabelBrush = Application.Current.Resources["BlackBrush"] as Brush;
            lineChartPlotter.VerticalAxis.LineAndLabelBrush = Application.Current.Resources["BlackBrush"] as Brush;
            lineChartPlotter.horizontalAxis.Plotter.BorderBrush = Application.Current.Resources["BlackBrush"] as Brush;
            lineChartPlotter.Foreground = Application.Current.Resources["BlackBrush"] as Brush;
            lineChartPlotter.Background = Application.Current.Resources["WhiteBrush"] as Brush;
            lineChartPlotter.Children.Add(new Microsoft.Research.DynamicDataDisplay.Charts.Navigation.CursorCoordinateGraph());
            lineChartPlotter.VerticalAlignment = VerticalAlignment.Stretch;
            lineChartPlotter.HorizontalAlignment = HorizontalAlignment.Stretch;
            Content = lineChartPlotter;
        }
        /// <summary>
        /// 设置主题：Dark/Light
        /// </summary>
        /// <param name="accent"></param>
        public void SetAccent(string accent)
        {
            if (accent == "Light")
            {
                lineChartPlotter.horizontalAxis.LineAndLabelBrush = Brushes.Black;
                lineChartPlotter.VerticalAxis.LineAndLabelBrush = Brushes.Black;
                lineChartPlotter.horizontalAxis.Plotter.BorderBrush = Brushes.Black;
                lineChartPlotter.Foreground = Brushes.Black;
                lineChartPlotter.Background = Brushes.White;
            }
            else if (accent == "Dark")
            {
                lineChartPlotter.horizontalAxis.LineAndLabelBrush = Brushes.White;
                lineChartPlotter.VerticalAxis.LineAndLabelBrush = Brushes.White;
                lineChartPlotter.horizontalAxis.Plotter.BorderBrush = Brushes.White;
                lineChartPlotter.Foreground = Brushes.White;
                SolidColorBrush mySolidColorBrush = new SolidColorBrush();
                mySolidColorBrush.Color = System.Windows.Media.Color.FromArgb(255, 30, 30, 30);
                lineChartPlotter.Background = mySolidColorBrush;
            }
            lineChartPlotter.Children.Add(new Microsoft.Research.DynamicDataDisplay.Charts.Navigation.CursorCoordinateGraph());
            lineChartPlotter.VerticalAlignment = VerticalAlignment.Stretch;
            lineChartPlotter.HorizontalAlignment = HorizontalAlignment.Stretch;
            Content = lineChartPlotter;
        }
        /// <summary>
        /// 清除所有数据
        /// </summary>
        public void ClearAllData()
        {
            foreach (var item in LineDictionary)
            {
                item.Value.DataSource.Clear();
            }
            LineDictionary.Clear();
            foreach (var item in MarkLineDictionary)
            {
                item.Value.xyDataSource.Clear();
                //item.Value.yDataSource.Collection.Clear();
            }
            MarkLineDictionary.Clear();
            foreach (var item in AynscLineLineDictionary)
            {
                item.Value.DataSource.Clear();
            }
            AynscLineLineDictionary.Clear();
            lineChartPlotter.RemoveUserElements();
            lineChartPlotter.Children.Add(new Microsoft.Research.DynamicDataDisplay.Charts.Navigation.CursorCoordinateGraph());
            XTitleFlag = false;
            YTitleFlag = false;
            HeaderFlag = false;
        }
        /// <summary>
        /// 清除当前曲线的数据
        /// </summary>
        public void ClearLineFirstUpdate(string LineName)
        {
            if (LineDictionary.ContainsKey(LineName))
            {
                Dispatcher.Invoke(DispatcherPriority.Render,
                new Action(() =>
                {
                    LineDictionary[LineName].DataSource.Collection.Clear();
                }));
            }
            else if (AynscLineLineDictionary.ContainsKey(LineName))
            {
                AynscLineLineDictionary[LineName].DataSource.Collection.Clear();
            }
            else if (MarkLineDictionary.ContainsKey(LineName))
            {
                MarkLineDictionary[LineName].xyDataSource.Collection.Clear();
            }
        }
        /// <summary>
        /// 设置折线图标题
        /// </summary>
        public void SetHeader(string Header)
        {
            if (!HeaderFlag)
            {
                Header header = new Header();
                TextBlock headerText = new TextBlock();
                headerText.Text = Header;
                headerText.FontSize = 15;
                headerText.FontWeight = FontWeights.Bold;
                //headerText.Foreground = Brushes.White;
                header.Content = headerText;
                lineChartPlotter.Children.Add(header);
                HeaderFlag = true;
            }
        }
        /// <summary>
        /// 设置折线图X轴标题和Y轴标题
        /// </summary>
        public void SetXYAxisTitle(string XAxisTitle, string YAxisTitle)
        {
            if (!XTitleFlag && XAxisTitle != string.Empty)
            {
                HorizontalAxisTitle HorizontalFooter = new HorizontalAxisTitle();
                TextBlock footerTextX = new TextBlock();
                footerTextX.FontSize = 12;
                footerTextX.FontStyle = FontStyles.Normal;
                //footerTextX.Foreground = Brushes.White;
                footerTextX.Text = XAxisTitle;
                HorizontalFooter.Content = footerTextX;
                lineChartPlotter.Children.Add(HorizontalFooter);
                XTitleFlag = true;
            }
            if (!YTitleFlag && YAxisTitle != string.Empty)
            {
                VerticalAxisTitle VerticalFooter = new VerticalAxisTitle();
                TextBlock footerTextY = new TextBlock();
                footerTextY.FontSize = 12;
                footerTextY.FontStyle = FontStyles.Normal;
                //footerTextY.Foreground = Brushes.White;
                footerTextY.Text = YAxisTitle;
                VerticalFooter.Content = footerTextY;
                lineChartPlotter.Children.Add(VerticalFooter);
                YTitleFlag = true;
            }
        }
        /// <summary>
        /// 增加一条线
        /// </summary>
        public void AddLine(string LineName, Color? color = null)
        {
            if (!LineDictionary.ContainsKey(LineName))
            {
                NormalLineInfo line = new NormalLineInfo();
                if (color == null)
                    line.LineGraph = lineChartPlotter.AddLineGraph(line.ds, 1.5, LineName);
                else
                    line.LineGraph = lineChartPlotter.AddLineGraph(line.ds, color.Value, 1.5, LineName);
                LineDictionary.Add(LineName, line);
            }
        }
        /// <summary>
        /// 删除一条线
        /// </summary>
        public void DeleteLine(string LineName)
        {
            if (LineDictionary.ContainsKey(LineName))
            {
                LineDictionary[LineName].DataSource.Clear();
                lineChartPlotter.DeleteLineGraph(LineDictionary[LineName].LineGraph);
                LineDictionary.Remove(LineName);
            }
        }
        /// <summary>
        /// 增加一条带Mark点的线  
        /// </summary>
        public void AddMarkLine(string LineName, Color? color = null)
        {
            if (!MarkLineDictionary.ContainsKey(LineName))
            {
                // Add data sources:
                lock (MarkLineDictionary)
                {
                    MarkLineInfo MarkLine = new MarkLineInfo();
                    if (!MarkLineDictionary.ContainsKey(LineName))
                    {
                        if (color == null)
                            MarkLine.chart = lineChartPlotter.AddLineGraph(MarkLine.ds,
                                    new Pen(new SolidColorBrush(ColorHelper.CreateRandomHsbColor()), 1),
                                    new CircleElementPointMarker
                                    {
                                        Size = 7,
                                        Brush = Brushes.Red,
                                        Fill = Brushes.Orange
                                    },
                                    new PenDescription(LineName));
                        else
                            MarkLine.chart = lineChartPlotter.AddLineGraph(MarkLine.ds,
                                    new Pen(new SolidColorBrush(color.Value), 1),
                                    new CircleElementPointMarker
                                    {
                                        Size = 7,
                                        Brush = Brushes.Red,
                                        Fill = Brushes.Orange
                                    },
                                    new PenDescription(LineName));

                        MarkLineDictionary.Add(LineName, MarkLine);
                    }
                }
            }
        }
        /// <summary>
        /// 增加一条同步线
        /// </summary>
        public void AddLine(string LineName, double DownLimit, double UpLimit, double Interval)
        {
            if (!AynscLineLineDictionary.ContainsKey(LineName))
            {
                if (DownLimit > UpLimit) Swap(ref UpLimit, ref DownLimit);
                AynscLineInfo AynscLine = new AynscLineInfo(DownLimit, UpLimit, Interval);
                AynscLineLineDictionary.Add(LineName, AynscLine);
                AynscLine.LineGraph = lineChartPlotter.AddLineGraph(AynscLine.ds, 1.5, LineName);
                SetXAxisRange(DownLimit, UpLimit);
            }
        }
        /// <summary>
        /// 向线中增加点
        /// </summary>
        public void AddPointToLine(string LineName, Point AddPoint)
        {
            lock (this)
            {
                switch (_dataTrans)
                {
                    case DataTransFormat.DelegateDataTransform:
                        AddPoint.X = SecondToDateTimeTicks(AddPoint.X);
                        break;
                    default:
                        break;
                }
                if (LineDictionary.ContainsKey(LineName))
                {
                    if (LineDictionary[LineName].DataSource.Collection.Where(a => a.X == AddPoint.X).Count() > 0)
                    {
                        Point temp = LineDictionary[LineName].DataSource.Collection.Where(a => a.X == AddPoint.X).FirstOrDefault();
                        int index = LineDictionary[LineName].DataSource.Collection.IndexOf(temp);
                        LineDictionary[LineName].DataSource.Collection.Remove(temp);
                        LineDictionary[LineName].DataSource.Collection.Insert(index, AddPoint);
                    }
                    else
                        LineDictionary[LineName].DataSource.Append(AddPoint);
                }
                else if (AynscLineLineDictionary.ContainsKey(LineName))
                {
                    AynscLineLineDictionary[LineName].ConvertDataSource(Dispatcher, AddPoint);
                }
                else if (MarkLineDictionary.ContainsKey(LineName))
                {
                    if (MarkLineDictionary[LineName].xyDataSource.Collection.Where(a => a.X == AddPoint.X).Count() > 0)
                    {
                        Point temp = MarkLineDictionary[LineName].xyDataSource.Collection.Where(a => a.X == AddPoint.X).FirstOrDefault();
                        int index = MarkLineDictionary[LineName].xyDataSource.Collection.IndexOf(temp);
                        MarkLineDictionary[LineName].xyDataSource.Collection.Remove(temp);
                        MarkLineDictionary[LineName].xyDataSource.Collection.Insert(index, AddPoint);
                    }
                    else
                        MarkLineDictionary[LineName].xyDataSource.Append(AddPoint);
                    //MarkLineDictionary[LineName].ConvertDataSource();
                }
            }
        }
        /// <summary>
        /// 修改线颜色和粗细
        /// </summary>
        /// <param name="name"></param>
        /// <param name="color"></param>
        /// <param name="thickness"></param>
        public void SetLineCP(string name, string color, float thickness)
        {
            if (LineDictionary.ContainsKey(name))
            {
                LineDictionary[name].LineGraph.LinePen = new Pen(new SolidColorBrush((Color)ColorConverter.ConvertFromString(color)), thickness);
                LineDictionary[name].LineGraph.Description = null;
            }
            else if (AynscLineLineDictionary.ContainsKey(name))
            {
                AynscLineLineDictionary[name].LineGraph.LinePen = new Pen(new SolidColorBrush((Color)ColorConverter.ConvertFromString(color)), thickness);
                AynscLineLineDictionary[name].LineGraph.Description = null;
            }
            else if (MarkLineDictionary.ContainsKey(name))
            {
                MarkLineDictionary[name].chart.LineGraph.LinePen = new Pen(new SolidColorBrush((Color)ColorConverter.ConvertFromString(color)), thickness);
                MarkLineDictionary[name].chart.LineGraph.Description = null;
            }
            lineChartPlotter.InvalidateVisual();
        }
        /// <summary>
        /// 向线中增加多点
        /// </summary>
        public void AddManyPointToLine(string LineName, IEnumerable<Point> AddPoint)
        {
            switch (_dataTrans)
            {
                case DataTransFormat.DelegateDataTransform:
                    List<Point> AddPoints = new List<Point>();
                    foreach (var item in AddPoint)
                    {
                        AddPoints.Add(new Point(SecondToDateTimeTicks(item.X), item.Y));
                    }
                    AddPoint = AddPoints;
                    break;
                default:
                    break;
            }
            if (LineDictionary.ContainsKey(LineName))
            {
                LineDictionary[LineName].DataSource.Clear();
                LineDictionary[LineName].DataSource.AppendMany(AddPoint);
            }
            else if (MarkLineDictionary.ContainsKey(LineName))
            {
                MarkLineDictionary[LineName].xyDataSource.Clear();
                MarkLineDictionary[LineName].xyDataSource.AppendMany(AddPoint);
            }
        }
        /// <summary>
        /// 设置固定Y轴范围
        /// </summary>
        public void SetYAxisRange(double DownLimit, double UpLimit)
        {
            if (DownLimit > UpLimit) Swap(ref UpLimit, ref DownLimit);
            Rect Temp = lineChartPlotter.Viewport.FixedRect;
            Temp.Y = DownLimit;
            Temp.Height = UpLimit - DownLimit;
            lineChartPlotter.Viewport.FixedRect = Temp;
        }
        /// <summary>
        /// 设置固定X轴范围
        /// </summary>
        public void SetXAxisRange(double DownLimit, double UpLimit)
        {
            if (DownLimit > UpLimit) Swap(ref UpLimit, ref DownLimit);
            Rect Temp = lineChartPlotter.Viewport.FixedRect;
            Temp.X = DownLimit;
            Temp.Width = UpLimit - DownLimit;
            lineChartPlotter.Viewport.FixedRect = Temp;
        }
        /// <summary>
        /// 增加固定标识
        /// </summary>
        public void AddUpLimit(double UpLimit)
        {
            lineChartPlotter.AddHorizontalLimit(UpLimit);
        }
        /// <summary>
        /// 输出文件
        /// </summary>
        /// <param name="filepath"></param>
        public void SavePicToFile(string filepath)
        {
            lineChartPlotter.SaveScreenshot(filepath);
        }
        /// <summary>
        /// 输出文件Source
        /// </summary>
        public System.Windows.Media.Imaging.BitmapSource SavePicToSource()
        {
            return lineChartPlotter.CreateScreenshot();
        }
        /// <summary>
        /// 选择显示曲线
        /// </summary>
        /// <param name="LineName"></param>
        /// <param name="flag"></param>
        public void SetLineDisplay(string LineName, bool flag)
        {
            lock (this)
            {
                if (LineDictionary.ContainsKey(LineName))
                {
                    LineDictionary[LineName].DisplayLine = flag;
                }
                else if (AynscLineLineDictionary.ContainsKey(LineName))
                {
                    AynscLineLineDictionary[LineName].DisplayLine = flag;
                }
                else if (MarkLineDictionary.ContainsKey(LineName))
                {
                    MarkLineDictionary[LineName].DisplayLine = flag;
                }
                lineChartPlotter.FitToView();
            }
        }
        /// <summary>
        /// 设置曲线类型
        /// </summary>
        /// <param name="lineType"></param>
        public static void SetLineType(LineType lineType)
        {
            Plotter2DExtensions.LineType = lineType;
        }
        /// <summary>
        /// 绑定Maker到某一条线
        /// </summary>
        /// <param name="LineName"></param>
        public void BindingMarker(string LineName)
        {
            if (LineDictionary.ContainsKey(LineName))
            {
                lineChartPlotter.markerDataPointGraph.DataSource = LineDictionary[LineName].DataSource;
            }
            else if (AynscLineLineDictionary.ContainsKey(LineName))
            {
                lineChartPlotter.markerDataPointGraph.DataSource = AynscLineLineDictionary[LineName].DataSource;
            }
            else if (MarkLineDictionary.ContainsKey(LineName))
            {
                lineChartPlotter.markerDataPointGraph.DataSource = MarkLineDictionary[LineName].xyDataSource;
            }
        }
        /// <summary>
        /// 增加一个Marker点
        /// </summary>
        /// <param name="PointX"></param>
        public void AddMarker(double PointX)
        {
            lineChartPlotter.AddMarker(PointX);
        }
        /// <summary>
        /// 设置No点为最大值
        /// </summary>
        /// <param name="No"></param>
        public void SetMarkerMax(int No)
        {
            lineChartPlotter.markerDataPointGraph.SetMakerPeak(No - 1);
        }
        /// <summary>
        /// 设置No点为最小值
        /// </summary>
        /// <param name="No"></param>
        public void SetMarkerMin(int No)
        {
            lineChartPlotter.markerDataPointGraph.SetMakerMin(No - 1);
        }
        /// <summary>
        /// 增加一个Delta值
        /// </summary>
        /// <param name="No"></param>
        /// <param name="PointX"></param>
        public void AddMarkerDelta(int No, double PointX)
        {
            lineChartPlotter.markerDataPointGraph.AddMakerDelta(No - 1, PointX);
        }
        /// <summary>
        /// 删除Marker点
        /// </summary>
        /// <param name="No"></param>
        public void DeleteMarker(int No)
        {
            lineChartPlotter.markerDataPointGraph.DeleteMarker(No - 1);
        }

        private double SecondToDateTimeTicks(double Seconed)
        {
            return Seconed / 1e3 + 62135625.6;
        }

        private void Swap(ref double a, ref double b)
        {
            double tmp = a;
            a = b;
            b = tmp;
        }
    }
    /// <summary>
    /// Line数据源内容
    /// </summary>
    public class NormalLineInfo
    {
        public NormalLineInfo()
        {
            DataSource = new ObservableDataSource<Point>();
            ds = new CompositeDataSource(DataSource);
        }
        public bool DisplayLine
        {
            set
            {
                if (!value)
                {
                    LineGraph.DataSource = new EmptyDataSource();
                }
                else
                {
                    LineGraph.DataSource = ds;
                }
            }
        }

        public IPointDataSource ds;
        public ObservableDataSource<Point> DataSource;
        public LineGraph LineGraph;
        public void ConvertDataSource()
        {
            return;
        }
    }
    /// <summary>
    /// MarkLine数据源内容
    /// </summary>
    public class MarkLineInfo
    {
        public MarkLineInfo()
        {
            xyDataSource = new ObservableDataSource<Point>();
            xyDataSource.SetXYMapping(X => X);
            xyDataSource.AddMapping(ShapeElementPointMarker.ToolTipTextProperty,
                Y => String.Format("({0},{1})", Y.X, Y.Y));
            ds = new CompositeDataSource(xyDataSource);
        }
        public bool DisplayLine
        {
            set
            {
                if (!value)
                {
                    chart.MarkerGraph.DataSource = new EmptyDataSource();
                    chart.MarkerGraph.DataSource = null;
                    chart.LineGraph.DataSource = new EmptyDataSource();
                }
                else
                {
                    chart.MarkerGraph.DataSource = ds;
                    chart.LineGraph.DataSource = ds;
                }
            }
        }
        public IPointDataSource ds;
        public LineAndMarker<ElementMarkerPointsGraph> chart;
        public ObservableDataSource<Point> xyDataSource;
        public void ConvertDataSource()
        {
            return;
        }
    }
    /// <summary>
    /// 异步更新Line数据源内容
    /// </summary>
    public class AynscLineInfo
    {
        public AynscLineInfo(double DownLimit, double UpLimit, double Interval)
        {
            // Add data sources:
            DataSource = new ObservableDataSource<Point>();
            this.DownLimit = DownLimit;
            this.UpLimit = UpLimit;
            this.Interval = Interval;
            this.PointNum = Convert.ToInt32((UpLimit - DownLimit) / Interval);
            ds = new CompositeDataSource(DataSource);
        }
        public bool DisplayLine
        {
            set
            {
                if (!value)
                {
                    LineGraph.DataSource = new EmptyDataSource();
                }
                else
                {
                    LineGraph.DataSource = ds;
                }
            }
        }
        public ObservableDataSource<Point> DataSource;
        public IPointDataSource ds;
        public LineGraph LineGraph;
        public double DownLimit, UpLimit, Interval;
        private int PointNum;
        public void ConvertDataSource(Dispatcher Dispatcher, Point point)
        {
            Dispatcher.Invoke((Action)(() =>
            {
                List<Point> Temp = DataSource.Collection.ToList(), NewTemp = new List<Point>();
                if (DataSource.Collection.Count == PointNum)
                {
                    Temp.RemoveAt(0);
                }
                DataSource.Collection.Clear();
                Temp.ForEach((x) => {
                    x.X -= Interval;
                    NewTemp.Add(x);
                });
                point.X = UpLimit;
                NewTemp.Add(point);
                Temp.Clear();
                Temp.Capacity = 0;
                DataSource.AppendMany(NewTemp);
            }));
        }
    }
}
